home *** CD-ROM | disk | FTP | other *** search
/ The Original Shareware 1.1 / The Original Shareware (WeMake CDs)(Volume 1.1)(CDs, Inc)(1993).iso / 19 / madtrb11.zip / DOS2IO-1.INC < prev    next >
Text File  |  1985-08-17  |  14KB  |  503 lines

  1.  
  2. (*
  3.                    Dos2io-1.inc
  4.  
  5.  
  6.     Dedicated to the public domain.
  7.                 -- Cole Brecheen
  8.                    17 August 1985
  9. *)
  10.  
  11. {$V-}   {Relaxes type checking on string parameters; must
  12.                       also be turned off in the main file.}
  13. {$U-,C-,R-} {Enables keyboard buffering.}
  14.  
  15. CONST
  16.   null = '';
  17.     {A vestige of IBM Pascal, which does not allow '' as a
  18.     string literal.  Helps distinguish '' from a literal
  19.     space.}
  20.  
  21.   BufSize = 255;
  22.   inp = 0;
  23.   outp = 1;
  24.     {The PC-DOS v2.0 manual at D-15 explains the significance
  25.     of these numbers.}
  26.  
  27. TYPE
  28.   ErrorMessage  = (
  29.            NoError,
  30.            BadFunction,
  31.            FileNotFound,
  32.            PathNotFound,
  33.            NoHandlesLeft,
  34.            AccessDenied,
  35.            BadHandle,
  36.            MCBsDestroyed,
  37.            TooLittleMemory,
  38.            BadMemBlockAddr,
  39.            BadEnvironment,
  40.            BadFormat,
  41.            BadAccessCode,
  42.            BadData,
  43.            MissingMessage,
  44.     {The PC-DOS v2.0 user's manual doesn't list a message
  45.     14.}
  46.            InvalidDrive,
  47.            CurrentDirErase,
  48.            DifferentDevice,
  49.            NoMoreFiles,
  50.     {These are the 18 MS-DOS standard error messages.  See
  51.     PC-DOS v2.0 user's manual at D-14.  The function
  52.     MessageType, below, depends on your keeping them in their
  53.     present order.  The messages below are added for
  54.     convenience.}
  55.            EndOfFile,
  56.            PartialRead
  57.           );
  58.  
  59.   BufType = PACKED ARRAY [1..BufSize] OF CHAR;
  60.  
  61.  
  62.   DataRegister =
  63.     RECORD
  64.       CASE BOOLEAN OF
  65.     TRUE :      ( l : BYTE ;
  66.           h : BYTE );
  67.     FALSE : ( x : INTEGER );
  68.       END;
  69.  
  70.   regpack =
  71.     RECORD
  72.       a, b, c, d: DataRegister;
  73.       bp, si, di, ds, es, flags: INTEGER;
  74.     END;
  75.     {This is the record type used with the msdos procedure.}
  76.  
  77.   dos2str80 = STRING[80];
  78.   dos2str255 = STRING[255];
  79.   dos2charset = set of CHAR;
  80.   dos2numset = set of 0..255;
  81.  
  82. VAR
  83.   choice :  CHAR;
  84.   TypeAheadLegal : BOOLEAN;
  85.  
  86.  
  87. FUNCTION MessageType( FunctionResult: byte ): ErrorMessage;
  88. VAR
  89.   converter : RECORD CASE BOOLEAN of
  90.         true : ( num : byte );
  91.         false : ( msg : ErrorMessage );
  92.           END;
  93. BEGIN {MessageType}
  94.   converter.num := FunctionResult;
  95.   MessageType := converter.msg;
  96.     {The case variant allows coverter to be referred to both
  97.     as a byte and as an ErrorMessage.}
  98. END; {MessageType}
  99.  
  100.  
  101. PROCEDURE WriteStr( FileHandle : INTEGER;
  102.             TheStr : dos2str255 ); forward;
  103.  
  104. PROCEDURE WriteEol( FileHandle : INTEGER ); forward;
  105.  
  106.  
  107. PROCEDURE abort( message : dos2str80 );
  108. BEGIN {abort}
  109.   WriteEol( outp );
  110.   WriteStr( outp, message );
  111.   WriteEol( outp );
  112.   WriteStr( outp, 'Press <return>.' );
  113.   readln;
  114.     {Can't use GetKey here because GetKey may abort for lack
  115.     of initialization.  A GetKey inside GetKey would cause
  116.     endless loop.}
  117.   halt;
  118. END;  {abort}
  119.  
  120.  
  121.  
  122. PROCEDURE PrintMessage( functionresult: errormessage );
  123. BEGIN {PrintMessage}
  124.   CASE functionresult OF
  125.     NoError : BEGIN END;
  126.     BadFunction : abort( 'Invalid function number.' );
  127.     FileNotFound : abort( 'File not found.' );
  128.     PathNotFound : abort( 'Path not found.' );
  129.     NoHandlesLeft : abort( 'No handles left.' );
  130.     AccessDenied : abort( 'Access denied.' );
  131.     BadHandle : abort( 'Invalid handle.' );
  132.     MCBsDestroyed : abort( 'Memory control blocks destroyed.' );
  133.     TooLittleMemory : abort( 'Insufficient memory.' );
  134.     BadMemBlockAddr : abort( 'Invalid memory block address.' );
  135.     BadEnvironment : abort( 'Invalid environment.' );
  136.     BadFormat : abort( 'Invalid format.' );
  137.     BadAccessCode : abort( 'Invalid access code.' );
  138.     BadData : abort( 'Invalid data.' );
  139.     MissingMessage : abort( 'Missing message 14.' );
  140.     InvalidDrive : abort( 'Invalid drive was specified.' );
  141.     CurrentDirErase : abort('Can''t remove current directory.');
  142.     DifferentDevice : abort( 'Different device.' );
  143.     NoMoreFiles : abort( 'No more files.' );
  144.  
  145.     PartialRead : write( 'Partial read.' );
  146.     EndOfFile : write( 'End of file.' );
  147.     {We don't have an else because, if the runtime system
  148.     decides that some different value is possible, we want to
  149.     know.}
  150.   END; {case}
  151. END; {PrintMessage}
  152.  
  153.  
  154. FUNCTION FlaggedError( TheFlags : INTEGER ): BOOLEAN;
  155.     {Detects the PC/MS-DOS error signal in the carry flag.}
  156. BEGIN
  157.   FlaggedError := odd( abs( TheFlags ) );
  158. END; {FlaggedError}
  159.  
  160.  
  161.  
  162. PROCEDURE AddStr( VAR first : dos2str255; second : dos2str255 );
  163.     {Concatenates the second string onto the end of the
  164.     first.  Requires less typing and executes more
  165.     efficiently than doing the same thing with Turbo's concat
  166.     function.}
  167. BEGIN
  168.   first[0] := succ( first[0] );
  169.   insert( second, first, length(first) );
  170.   first[0] := pred( first[0] );
  171. END; {AddStr}
  172.  
  173.  
  174.  
  175. PROCEDURE MakeAsciiZ( VAR TheStr : dos2str255 );
  176. VAR
  177.   lngth,
  178.   index : INTEGER;
  179. BEGIN
  180.   IF lngth > 0 THEN 
  181.     FOR index := 0 TO (lngth - 1) DO
  182.       BEGIN
  183.         TheStr[ index ] := TheStr[ index + 1 ];
  184.       END;
  185.   TheStr[lngth] := #0;
  186. END; {MakeAsciiZ}
  187.  
  188.  
  189.  
  190. PROCEDURE WriteStr{ FileHandle : INTEGER; TheStr : dos2str255 };
  191. VAR
  192.   rgstr : regpack;
  193. BEGIN {WriteStr}
  194.   IF FileHandle = inp
  195.     THEN abort( 'Cannot write to standard input.' );
  196.   with rgstr DO BEGIN
  197.     b.x := FileHandle;
  198.     c.x := length( TheStr );
  199.     MakeAsciiZ( TheStr );
  200.     ds := seg( TheStr );
  201.     d.x := ofs( TheStr );
  202.     a.h := $40;  {Write to a file or device command}
  203.     msdos( rgstr );
  204.     IF FlaggedError( flags )
  205.       THEN PrintMessage( MessageType( a.x ) );
  206.     IF a.x < c.x  {if fewer than c.x bytes were actually written}
  207.       THEN abort( 'No room to write.' );
  208.   END; {with rgstr}
  209. END;  {WriteStr}
  210.  
  211.  
  212.  
  213. FUNCTION IntStr( TheNumber : INTEGER;
  214.          StrLngth: INTEGER ): dos2str80;
  215. VAR
  216.   BufStr : dos2str80;
  217. BEGIN {IntStr}
  218.   str( TheNumber:StrLngth, BufStr );
  219.     {See the Turbo Pascal manual at p. 108 for an
  220.     explanation of how StrLngth functions in this statement.}
  221.   IntStr := BufStr;
  222. END; {IntStr}
  223.  
  224.  
  225. FUNCTION RealStr( TheNumber : REAL;
  226.           StrLngth,
  227.           DigitsAfterDecimal : INTEGER ): dos2str80;
  228. VAR
  229.   BufStr : dos2str80;
  230. BEGIN {RealStr}
  231.   str( TheNumber:StrLngth:DigitsAfterDecimal, BufStr );
  232.   RealStr := BufStr;
  233. END; {RealStr}   
  234.  
  235.  
  236. PROCEDURE WriteEol{ FileHandle : INTEGER };
  237. BEGIN {WriteEol}
  238.   WriteStr( FileHandle, concat( #13, #10 ) );
  239. END;  {WriteEol} 
  240.  
  241.  
  242.  
  243. TYPE
  244.   BufferPtr = ^FileBuff;
  245.     {This is the record type that ReadStr uses to perform
  246.     dynamic file buffering.}
  247.  
  248.   FileBuff = RECORD
  249.            buf : buftype;
  250.     {There's a mysterious problem lurking here somewhere that
  251.     takes an eggbeater to the heap if buf is made the last
  252.     item in this record.  It doesn't seem to show up if buf
  253.     is the first item.}
  254.  
  255.            prev,
  256.            next : BufferPtr;
  257.            ndx,
  258.            handle, size : INTEGER;
  259.            eof : BOOLEAN;
  260.          END;
  261. VAR
  262.   BufLstBase : BufferPtr;
  263.   Dos2ioInitKey : REAL;
  264.  
  265.  
  266. PROCEDURE InitDos2io;
  267. BEGIN
  268.   TypeAheadLegal := true;
  269.   BufLstBase := nil;
  270.   Dos2ioInitKey := 5721.0;
  271. END; {InitDos2io}
  272.  
  273. PROCEDURE CheckInitialization;
  274. BEGIN
  275.   IF Dos2ioInitKey <> 5721.0 THEN
  276.     abort( 'Please initialize with InitDos2io.' );
  277. END; {CheckInitialization}
  278.  
  279.  
  280. PROCEDURE ReadStr( FileHandle : INTEGER;     
  281.            VAR TheStr : dos2str255 );        
  282. LABEL EndProcedure;
  283. VAR
  284.   BufPtr : BufferPtr;
  285.   Strlong: dos2str255;
  286.  
  287.   PROCEDURE load( VAR BufPtr : BufferPtr );
  288.   VAR
  289.     rgstr : regpack;
  290.     eofpos : INTEGER;
  291.   BEGIN
  292.     with rgstr DO BEGIN
  293.       b.x := FileHandle;
  294.       c.x := BufSize;
  295.     {CX gets the number of bytes to be transferred.}
  296.       ds := seg( BufPtr^.buf );
  297.       d.x := ofs( BufPtr^.buf );
  298.       a.h := $3F;
  299.     {Read from a file or device command.}
  300.       msdos( rgstr );
  301.       IF FlaggedError( flags )
  302.     THEN BEGIN writeln('readstr error');  {diag}
  303.       PrintMessage( MessageType( a.x ) );
  304.       END;
  305.       BufPtr^.size := a.x;
  306.     {AX contains the number of bytes actually transferred.
  307.     If the value is zero, the program has tried to read from
  308.     the end of file. }
  309.       IF FileHandle <> 0 THEN BufPtr^.ndx := BufPtr^.ndx - BufSize
  310.         ELSE BufPtr^.ndx := 1;
  311.       eofpos := pos(#26,BufPtr^.buf);
  312.       IF eofpos <> 0 THEN  BEGIN
  313.         BufPtr^.buf[eofpos] := #13;
  314.         BufPtr^.size := eofpos - 1;
  315.       END;
  316.       IF BufPtr^.size = BufSize THEN BufPtr^.size := BufSize + 3;
  317.       BufPtr^.eof := BufPtr^.size = 0;
  318.     END; {with rgstr}
  319.   END; {load}
  320.  
  321.   PROCEDURE MakeBuffFor( FileHandle : INTEGER );
  322.   LABEL
  323.     EndProcedure;
  324.   VAR
  325.     OldPtr, TmpPtr : BufferPtr;
  326.   BEGIN
  327.     IF BufLstBase = nil THEN
  328.       BEGIN
  329.     {If there are no file buffers in the BufLst at all (i.e,
  330.     if BufLstBase is nil), the next few lines will create the
  331.     first buffer and put its address in BufLstBase.}
  332.     new( TmpPtr );
  333.     BufLstBase := TmpPtr;
  334.     TmpPtr^.ndx := BufSize + 1;
  335.     TmpPtr^.next := nil;
  336.     TmpPtr^.prev := nil;
  337.     TmpPtr^.handle := FileHandle;
  338.     GOTO EndProcedure;
  339.       END;
  340.  
  341.     TmpPtr := BufLstBase;
  342.  
  343.     WHILE (TmpPtr^.handle <> FileHandle)
  344.       and
  345.       (TmpPtr^.next <> nil)
  346.       DO TmpPtr := TmpPtr^.next;
  347.     {This checks to see whether a buffer for this file is
  348.     already in the BufLst.  If not, the if-then construct
  349.     immediately below will create one and add it to the
  350.     BufLst.}
  351.  
  352.     IF TmpPtr^.handle <> FileHandle THEN
  353.       BEGIN
  354.     OldPtr := TmpPtr;
  355.     new( TmpPtr );
  356.     OldPtr^.next := TmpPtr;
  357.     TmpPtr^.prev := OldPtr;
  358.     TmpPtr^.ndx := BufSize + 1;
  359.     TmpPtr^.next := nil;
  360.     TmpPtr^.handle := FileHandle;
  361.       END;
  362.  
  363.     EndProcedure:
  364.     BufPtr := TmpPtr;
  365.   END;  {MakeBuffFor}
  366.  
  367.   PROCEDURE MoveLine( VAR BufPtr : BufferPtr;
  368.               VAR TheLine : dos2str255 );
  369.     {Takes one line from the buffer and puts it in TheLine.}
  370.   VAR  endstr: INTEGER;
  371.   LABEL EndProcedure;
  372.   BEGIN
  373.     TheLine := copy(BufPtr^.buf,BufPtr^.ndx,BufSize - BufPtr^.ndx + 1);
  374.     endstr :=pos(#13,TheLine);
  375.     IF endstr = 0 THEN BEGIN   {no CR in rest of current buffer}
  376.       IF BufPtr^.size > BufSize THEN BEGIN  {eof not in current buffer}
  377.         BufPtr^.ndx := BufSize + 1;
  378.         load( BufPtr );
  379.         endstr := pos(#13, BufPtr^.buf);
  380.         IF endstr = 0 THEN     {no CR in new buufer either}
  381.           IF BufPtr^.size < BufSize THEN BEGIN  {if eof in this buffer}
  382.             endstr := BufPtr^.size + 1;
  383.             BufPtr^.eof := TRUE;
  384.           END  {IF BufPtr^.size < BufSize}
  385.           ELSE  abort('line too long:' + copy(TheLine,1,60) + '...');
  386.         TheLine := TheLine + copy(BufPtr^.buf,1,endstr -1);
  387.         BufPtr^.ndx := endstr + 2; {jumps over following LF}
  388.       END
  389.       ELSE BEGIN  {eof is in current buffer}
  390.         TheLine[0] := Chr(BufPtr^.size);
  391.         BufPtr^.eof := TRUE;
  392.       END;
  393.     END   {IF endstr = 0 }
  394.     ELSE BEGIN    {CR is in current buffer}
  395.       TheLine[0] := Chr(endstr - 1);
  396.       BufPtr^.ndx := BufPtr^.ndx + endstr + 1; {+1 jumps over following LF}
  397.     END;
  398.     EndProcedure:
  399.     END; {MoveLine}
  400.  
  401. BEGIN {ReadStr}
  402.   CheckInitialization;
  403.   IF FileHandle = outp
  404.     THEN abort( 'Cannot read from standard output.' );
  405.   TheStr := null;
  406.   MakeBuffFor( FileHandle );
  407.   IF (BufPtr^.ndx > BufSize) OR (FileHandle = 0) THEN load( BufPtr ); 
  408.   MoveLine( BufPtr, Strlong );
  409.   IF (BufPtr^.ndx > BufPtr^.size) AND (FileHandle <> 0) THEN BufPtr^.eof := TRUE
  410.   ELSE IF (BufPtr^.ndx > BufSize) AND (FileHandle <> 0) THEN load( BufPtr ); 
  411.   TheStr := Strlong;
  412. END;  {ReadStr}
  413.  
  414.  
  415. FUNCTION EndReached( FileHandle : INTEGER ): BOOLEAN;
  416. LABEL ErrorLabel;
  417. VAR
  418.   TmpPtr : BufferPtr;
  419. BEGIN
  420.   CheckInitialization;
  421.   TmpPtr := BufLstBase;
  422.   IF TmpPtr = nil
  423.     THEN GOTO ErrorLabel;
  424.   WHILE (TmpPtr^.handle <> FileHandle)
  425.     and
  426.     (TmpPtr^.next <> nil)
  427.     DO TmpPtr := TmpPtr^.next;
  428.   IF TmpPtr^.handle <> FileHandle
  429.     THEN
  430.       ErrorLabel:
  431.       abort('ReadStr(handle) must precede EndReached(handle).');
  432.   EndReached := TmpPtr^.eof;
  433. END; {EndReached}
  434.  
  435.  
  436.  
  437. PROCEDURE GetKey( VAR ch:CHAR; ReturnOnMatch: dos2charset );
  438. VAR
  439.   rgstr : regpack;
  440. BEGIN {GetKey}
  441.   CheckInitialization;
  442.   IF TypeAheadLegal
  443.     {Works only if U- and C- compiler directives are set in
  444.     all files.}
  445.     THEN rgstr.A.H := 8
  446.     {Console input without echo.}
  447.     ELSE rgstr.A.H := $C;
  448.     {Clear standard input buffer and invoke the input
  449.     function stored in AL.      See D-20 of PC-DOS 2.0 manual.}
  450.   REPEAT
  451.     rgstr.A.L := 8;
  452.     msdos( rgstr );
  453.     ch := CHR(rgstr.A.L);
  454.   UNTIL ch IN ReturnOnMatch;
  455. END; {GetKey}
  456.  
  457.  
  458.  
  459. PROCEDURE GetExtendedKey( VAR ch    : CHAR;
  460.                   legalchars : dos2charset;
  461.                   legalxkeys : dos2numset;
  462.                   VAR xkeygotten : BOOLEAN );
  463. VAR
  464.   byte1,
  465.   byte2 : BYTE;
  466.   rgstr : regpack;
  467.  
  468. BEGIN {GetExtendedKey}
  469.   CheckInitialization;
  470.   IF TypeAheadLegal
  471.     {Works only if U- and C- compiler directives are set in
  472.     all files.}
  473.     THEN rgstr.A.H := 8
  474.     ELSE rgstr.A.H := $C;
  475.  
  476.   REPEAT
  477.     rgstr.A.L := 8;
  478.     MsDos( rgstr );
  479.     byte1 := rgstr.A.L;
  480.     IF CHR(byte1) = CHR(0)
  481.       THEN
  482.     BEGIN
  483.       rgstr.A.H := 8;
  484.       MsDos( rgstr );
  485.       byte2 := rgstr.A.L;
  486.     END;
  487.   UNTIL ( CHR(byte1) IN legalchars)
  488.     OR
  489.     ( (byte1 = 0) AND (ORD(byte2) IN legalxkeys)); 
  490.   IF CHR(byte1) = CHR(0)
  491.     THEN
  492.       BEGIN
  493.         ch := CHR(byte2);
  494.         xkeygotten := TRUE;
  495.       END
  496.     ELSE
  497.       BEGIN
  498.         ch := CHR(byte1);
  499.         xkeygotten := FALSE;
  500.       END;
  501. END; {GetExtendedKey}
  502. 
  503.